package org.jproggy.pagesally;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.jproggy.snippetory.Template;

public class Fragment {
	private static final String CONTENT = "content";
	private final Fragment parent;
	private final Template snippet;
	private Collection<String> bound = new ArrayList<String>();
	private Template contentTarget = null; 
	
	public Fragment(Fragment parent, Template snippet) {
		this.parent = parent;
		this.snippet =  snippet;
	}
	
	public Template resolve( ) {
		resolve(new HashMap<String, Fragment>());
		return handleContent(null);
	}
	
	private void resolve( Map<String, Fragment> values) {
		resolveParent(values);
		resolveValues(values);
	}

	private void resolveValues(Map<String, Fragment> values) {
		for (String region: snippet.regionNames()) {
			if (!bound.contains(region)) {
				Template r = snippet.get(region);
				for (String val : r.names()) {
					if (CONTENT.equals(val)) {
						contentTarget = r;
						continue;
					} 
					Fragment source = values.get(val);
					if (source != null) {
						if (source.bind(val, r)) values.remove(val);
					}
				}
				if (r != contentTarget) r.render();
			}
		}
	}
	
	private Template handleContent(Fragment child) {
		if (child != null) {
			if (contentTarget != null) {
				if (child.snippet.regionNames().contains(CONTENT)) {
					child.snippet.get(CONTENT).render(contentTarget, CONTENT);
				} else {
					child.snippet.render(contentTarget, CONTENT);
				}
				contentTarget.render();
			} else {
				if (child.snippet.regionNames().contains(CONTENT)) {
					child.snippet.get(CONTENT).render(snippet, CONTENT);
				} else {
					child.snippet.render(snippet, CONTENT);
				}
				
			}
		}
		if (parent != null) return parent.handleContent(this);
		return snippet;
	}

	private void resolveParent(Map<String, Fragment> values) {
		if (parent != null) {
			parent.resolveChild(this);
			Map<String, Fragment> pVals = new HashMap<String, Fragment>(values);
			for (String region: snippet.regionNames()) {
				if (!bound.contains(region)) pVals.put(region, this);
			}
			parent.resolve(pVals);
		}
	}
	
	private boolean bind(String region, Template target) {
		if (bound.contains(region)) return false;
		if (CONTENT.equals(region)) {
			throw new IllegalArgumentException("Content needs special handling");
		}
		snippet.get(region).render(target, region);
		bound.add(region);
		return true;
	}
	
	private void resolveChild(Fragment child) {
		Collection<String> myNames = snippet.names();
		for (String region : child.snippet.regionNames()) {
			if (myNames.contains(region) && !CONTENT.equals(region)) {
				if (child.bind(region, snippet)) bound.add(region);
			} 
		}
		
		if (parent != null) parent.resolveChild(child);
	}
}
